package org.ucas.kgraph.service;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.ucas.kgraph.dao.AccountDao;
import org.ucas.kgraph.dao.CompanyDao;
import org.ucas.kgraph.dao.RoleDao;
import org.ucas.kgraph.dao.UserProfileDao;
import org.ucas.kgraph.entity.Account;
import org.ucas.kgraph.entity.Company;
import org.ucas.kgraph.entity.Role;
import org.ucas.kgraph.entity.UserProfile;
import org.ucas.kgraph.utils.exception.ConflictException;
import org.ucas.kgraph.utils.exception.ForbiddenException;
import org.ucas.kgraph.utils.exception.NotFoundException;
import org.ucas.kgraph.utils.common.DigestUtil;
import org.ucas.kgraph.utils.common.EncodeUtil;

import org.apache.shiro.subject.Subject;
//import javax.security.auth.Subject;
import javax.transaction.Transactional;

import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;

@Transactional
@Service
public class UserService {

  @Autowired
  private AccountDao accountDao;

  @Autowired
  private RoleDao roleDao;
  
  @Autowired
  private UserProfileDao userProfileDao;
  
  @Autowired
  private CompanyDao companyDao;

  public static final String HASH_ALGORITHM = "SHA-1";
  public static final int HASH_INTERATIONS = 1024;
  private static final int SALT_SIZE = 8;
  private static Logger logger = Logger.getLogger(UserService.class);

  public Optional<Account> findByName(String name) {
    return Optional.ofNullable(accountDao.findByName(name));
  }

  public Optional<Account> findBySessionName() {
	  return this.findByName(SecurityUtils.getSubject().getSession().getAttribute("userName").toString());
  }
  
  public Optional<Account> load(Long id) {
    return Optional.ofNullable(accountDao.findOne(id));
  }

  public void disable(Long id) {
    Optional<Account> account = load(id);
    if(!account.isPresent()) throw NotFoundException.User_NotExists;
    account.get().setDisabled(true);
    accountDao.save(account.get());
  }
  
  public void active(Long id) {
	    Optional<Account> account = load(id);
	    account.get().setDisabled(false);
	    accountDao.save(account.get());
  }
  public void disable(Account account) {
    disable(account.getId()); 
  }
  
  public void delete(Long id){
	  Account account =accountDao.findOne(id);
	  if(account.isDisabled()){
		  delete(account);
	  }else{
		  throw NotFoundException.User_NotExists;
	  }
  }
  public void delete(Account account){

	accountDao.delete(account);
    userProfileDao.delete(account.getUserProfile());
  }

  private boolean exists(Account account){
    Optional<Account> tmp;
    if(account.getId()!=null && load(account.getId()).isPresent())
      return true;
    if(accountDao.findByName(account.getName())!=null)
      return true;
    return false;
  }
  private boolean existscom(String comname){
	    if(companyDao.findByName(comname)!=null)
	      return true;
	    return false;
	  }
  /**
   * 设定安全的密码，生成随机的salt并经过1024次 sha-1 hash
   */
  private void encryptPassword(Account account) {
    byte[] salt = DigestUtil.generateSalt(SALT_SIZE);
    account.setSalt(EncodeUtil.encodeHex(salt));
    byte[] hashPassword = DigestUtil.sha1(account.getPlainPassword().getBytes(), salt, HASH_INTERATIONS);
    account.setPassword(EncodeUtil.encodeHex(hashPassword));
  }

  public Account create(Account account,String com_id){
	if(exists(account)) throw ConflictException.User_Confilict;
	Company company=new Company();
	company=companyDao.findByCom_id(com_id,"1");
	if(company==null) throw ForbiddenException.WRONG_COM;
	UserProfile profile=new UserProfile();
	profile.setCompany(company);
	userProfileDao.save(profile);
    encryptPassword(account);
    account.setUserProfile(profile);
    accountDao.save(account);
    return account;
  }
  public String getComId(){
	  Random random = new Random();
	  int code = random.nextInt(899999);
	  code = code+100000;
	  SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
	  String comid = code+df.format(new Date()).toString();
	  return comid;
  }
  
  public Company createCompany(Account account,String comname) {
	  if(exists(account)) throw ConflictException.User_Confilict;
	  if(existscom(comname)) throw ConflictException.Com_Confilict;
	  	Company companytmp=new Company();
	  	String comid="";
	  	while(companytmp!=null){
	  		comid=getComId();
	  		companytmp=companyDao.findByCom_id(comid);
	  	}
		Company company=new Company();
		company.setCom_id(comid);
		company.setCom_name(comname);
		company.setPlainpassword(account.getPlainPassword());
		company.setTelenumber(account.getName());
		company.setType("0");
		companyDao.save(company);
	    return company;
  }
  
  public Company getCompanyByComId(String com_id){
	  return companyDao.findByCom_id(com_id);
  }
  public boolean verify(Account account){
    Long accountId = verify(account.getName(),account.getPlainPassword());
    if(accountId!=null) {
      account.setId(accountId);
      return true;
    }else return false;
  }
//password是经过处理的
  public Long verify(String account, String password){
    Account tmp = accountDao.findByName(account);
    if(tmp == null) throw NotFoundException.User_NotExists;
    String encryptedPassword = EncodeUtil.encodeHex(DigestUtil.sha1(password.getBytes(), EncodeUtil.decodeHex(tmp.getSalt()), HASH_INTERATIONS));
    if(tmp.getPassword().equals(encryptedPassword)){

      return tmp.getId();
    }
    return null;
  }

  public void login(Account account){
	//if(verify(account)){
		UsernamePasswordToken token = new UsernamePasswordToken(account.getName(),account.getPlainPassword().toCharArray());
	    token.setRememberMe(true);
	   
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            Session session =  subject.getSession();
            session.setAttribute("userName", token.getUsername());
        } catch (IncorrectCredentialsException e) {
        	throw ForbiddenException.WRONG_TOKEN;
           
        } catch (DisabledAccountException e) {
        	throw ForbiddenException.DISABLED_ACCOUNT;
            
        }catch (UnknownAccountException e) {
        	throw  NotFoundException.User_NotExists;
        }
       
	    //SecurityUtils.getSubject()isAuthenticated();
	//}else{
	 
	//}
       
    
  }
  
  
  public void admlogin(Account account){
	//if(verify(account)){
		UsernamePasswordToken token = new UsernamePasswordToken(account.getName(),account.getPlainPassword().toCharArray());
	    token.setRememberMe(true);

        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            Session session =  subject.getSession();
            session.setAttribute("userName", token.getUsername());
            
        } catch (IncorrectCredentialsException e) {
        	throw ForbiddenException.WRONG_TOKEN;
           
        } catch (DisabledAccountException e) {
        	throw ForbiddenException.DISABLED_ACCOUNT;
            
        }catch (UnknownAccountException e) {
        	throw  NotFoundException.User_NotExists;
        }catch (UnauthorizedException e) {
        	throw  ForbiddenException.FORBIDDEN;
        }
  }
  public UserProfile update(UserProfile profile){
    valid(profile);
    if(profile.getId()!=null) userProfileDao.save(profile);
    else throw NotFoundException.User_NotExists;
    return profile;
  }


  /**
   * 验证Profile中的取值合法性
   * @param profile
   */
  private void valid(UserProfile profile){
    // TODO
  }

  public void changePassword(Long id, String newPassword){
    Account account = accountDao.findOne(id);
    if(account==null) throw NotFoundException.User_NotExists;
    account.setPassword(newPassword);
    String password = EncodeUtil.encodeHex(DigestUtil.sha1(newPassword.getBytes(), EncodeUtil.decodeHex(account.getSalt()), HASH_INTERATIONS));
    account.setPassword(password);
    accountDao.save(account);
  }
  
  public Optional<UserProfile> loadProfile(Long id){
    return Optional.ofNullable(userProfileDao.findOne(id));
  }

  public Optional<UserProfile> profile(Long accountId){
    return Optional.ofNullable(accountDao.findOne(accountId))
            .map(a -> a.getUserProfile());
  }

  public List<Role> getRoles(String name) {
	return roleDao.findByName(name);
  } 

  public List getAllUsers() {
	return accountDao.findAll();
  }

  public void save(Optional<Account> account) {
	// TODO Auto-generated method stub
	accountDao.save(account.get());
  }

  public Account newpassword(Account account, String opassword, String npassword) {
	
	byte[] salt = EncodeUtil.decodeHex(account.getSalt());
    byte[] hashPassword = DigestUtil.sha1(opassword.getBytes(), salt, HASH_INTERATIONS);
    if(EncodeUtil.encodeHex(hashPassword).equals(account.getPassword())){
		account.setPlainPassword(npassword);
		encryptPassword(account);
		accountDao.save(account);
	}else{
		throw ForbiddenException.WRONG_TOKEN;
	}
	return account;
  }
  public Account findpassword(Account account, String npassword) {
	byte[] salt = EncodeUtil.decodeHex(account.getSalt());
	account.setPlainPassword(npassword);
	encryptPassword(account);
	accountDao.save(account);
	return account;
  }
  
  public void editUserInfo(Optional<Account> account) {
	// TODO Auto-generated method stub
	  Account accountf = accountDao.findByName(account.get().getName());
	  if(accountf!=null&&!accountf.getId().equals(account.get().getId())){
		  throw ConflictException.User_Confilict;
	  }
	  accountDao.save(account.get());
  }
  
 


}
